package com.gitee.wsl.mathematics.geometry.d2.contour.ext

import com.gitee.wsl.mathematics.geometry.d2.contour.ContourPoint
import com.gitee.wsl.mathematics.geometry.d2.contour.ContourShape
import com.gitee.wsl.mathematics.geometry.d2.contour.ShapeContour
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.Segment2D
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.SegmentPoint
import com.gitee.wsl.mathematics.geometry.d2.contour.segment.toCurve2
import com.gitee.wsl.mathematics.geometry.d2.contour.util.Intersections
import kotlin.jvm.JvmName

/** Calculates a [List] of all intersections between the [ShapeContour] and another [ShapeContour]. */
@Suppress("unused")
fun ShapeContour.intersections(other: ShapeContour) = intersections(this, other)


/** Calculates a [List] of all intersections between the [ShapeContour] and a [Segment2D]. */
@Suppress("unused")
fun ShapeContour.intersections(other: Segment2D) = intersections(this, other.contour)

/** Calculates a [List] of all intersections between the [ShapeContour] and a [ContourShape]. */
@Suppress("unused")

fun ShapeContour.intersections(other: ContourShape) = intersections(this.shape, other)


/**
 * Calculates a [List] of all points of where paths intersect between two [ShapeContour]s.
 */
@JvmName("intersectionsShapeContour")
fun intersections(a: ShapeContour, b: ShapeContour, vertexThreshold: Double = 1E-5): List<ContourIntersection> {
    val selfTest = a === b
    val result = mutableListOf<ContourIntersection>()

    if (a.empty || b.empty) {
        return emptyList()
    }

    val lastA = a.segments.lastIndex
    val lastB = b.segments.lastIndex
    // this is where we should use a sweepline approach
    for ((ia, sa) in a.segments.withIndex()) {
        for ((ib, sb) in b.segments.withIndex()) {
            if (selfTest && ib > ia) {
                continue
            }
            val segmentIntersections = intersections(sa, sb, vertexThreshold).let {
                if (selfTest) {
                    it.filterNot { intersection -> intersection.a.segmentT == 1.0 && intersection.b.segmentT == 0.0 || intersection.a.segmentT == 0.0 && intersection.b.segmentT == 1.0 }
                } else {
                    it
                }
            }
            result.addAll(segmentIntersections.map {
                val at = if (it.a.segmentT == 1.0 && ia != lastA) 0.0 else it.a.segmentT
                val ai = if (it.a.segmentT == 1.0 && ia != lastA) ia + 1 else ia
                val bt = if (it.b.segmentT == 1.0 && ib != lastB) 0.0 else it.b.segmentT
                val bi = if (it.b.segmentT == 1.0 && ib != lastB) ib + 1 else ib

                ContourIntersection(
                    ContourPoint(a, (ai + at) / a.segments.size, a.segments[ai], at, it.position),
                    ContourPoint(b, (bi + bt) / b.segments.size, b.segments[bi], bt, it.position),
                    it.position
                )
            })
        }
    }


    return result.let {
        if (selfTest) {
            it.distinctBy { intersection -> Pair(intersection.a.contourT.toString().take(7), intersection.b.contourT.toString().take(7)) }
        } else {
            it
        }
    }
}

/**
 * Calculates a [List] of all points of where paths intersect between the two [ContourShape]s.
 */
@JvmName("intersectionShapeContour")
fun intersections(a: ContourShape, b: ContourShape): List<ContourIntersection> {
    return a.contours.flatMap { ac ->
        b.contours.flatMap { bc ->
            intersections(ac, bc)
        }
    }
}

/** Calculates a [List] of all points where two [Segment2D]s intersect. */
fun intersections(a: Segment2D, b: Segment2D, vertexThreshold: Double = 1E-5): List<SegmentIntersection> {

    if ((a.linear && a.length == 0.0f) || (b.linear && b.length == 0.0f)) {
        return emptyList()
    }


    // Test if checking against self. This test should be improved such that it is not based on object identity
    val selfTest = a === b
    val ca = a.toCurve2()
    val cb = b.toCurve2()

    return if (!selfTest) {
        Intersections.intersections(ca, cb).map {
            val at = when {
                it.x < vertexThreshold -> 0.0
                it.x >= 1.0 - vertexThreshold -> 1.0
                else -> it.x
            }
            val bt = when {
                it.y < vertexThreshold -> 0.0
                it.y >= 1.0 - vertexThreshold -> 1.0
                else -> it.y
            }
            val pointA = SegmentPoint(a, at.toDouble(), ca.position(it.x.toDouble()))
            val pointB = SegmentPoint(b, bt.toDouble(), pointA.position)
            SegmentIntersection(pointA, pointB, pointA.position)
        }
    } else {
        // Here we should handle self-intersections properly
        emptyList()
    }
}